#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/poll.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>

#define TTY1 		"./tty11"
#define TTY2 		"./tty12"
#define BUFFSIZE 	1024

/*
 * 定义任务运行的状态
 * */
enum {
	STATE_R = 1,
	STATE_W,
	STATE_AUTO,
	STATE_EX,
	STATE_T
};

/*
 * 定义任务-包含任务状态，和任务数据
 * */
struct fsm_st {
	int state;
	int sfd;
	int dfd;
	int len;
	int pos;
	char buf[BUFFSIZE];
	char *erstr;
};

/*
 * 状态机核心部分
 * */
static void fsm_driver(struct fsm_st * fsm)
{
	int ret;
	switch (fsm->state) {
		case STATE_R:
			fsm->len = read(fsm->sfd, fsm->buf, BUFFSIZE);
			printf("read data len: %d \n",fsm->len);
			if(fsm->len == 0){
				// 没有读取到数据，继续读
				fsm->state = STATE_R;
			} else if(fsm->len < 0) {
				// 读取伪错误
				if(errno == EAGAIN){
					fsm->state = STATE_R;
				} else if(errno == EINTR){
					fsm->state = STATE_R;
				} else {
					fsm->erstr = "read() failed";
					fsm->state = STATE_EX;
				}
			} else {
				// 读取到数据
				fsm->pos = 0;
				fsm->state = STATE_W;
			}
			break;
		case STATE_W:
			ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
			printf("write data len: %d \n",ret);
			if(ret < 0){
				// 写入伪错误 
				if(errno == EAGAIN){
					fsm->state = STATE_W;
				} else if(errno == EINTR){
					fsm->state = STATE_W;
				} else {
					fsm->erstr = "write() failed";
					fsm->state = STATE_EX;
				}
			} else {
				fsm->pos += ret; // 记录已经写入的位置
				fsm->len -= ret; // 记录剩余数量量
				if(fsm->len == 0){
					// 写入完成，改变状态去读取
					fsm->state = STATE_R;
				} else {
					// 写入未完成，继续写入
					fsm->state = STATE_W;
				}
			}
			break;
		case STATE_EX:
			perror(fsm->erstr);
			// 状态机推到终止状态
			fsm->state = STATE_T;
			break;
		case STATE_T:
			printf("job state: STATE_T \n");
			break;
		default:
			printf("job state: default \n");
			abort();
			break;
	}
}

/*
 * 准备数据，将任务放到状态机里面
 * */
static void relay(int fd1, int fd2)
{
	int fd1_save, fd2_save;
	struct fsm_st fsm12, fsm21;

	struct pollfd pfd[2];	// 定义需要2个pollfd来监控fsm

	fd1_save = fcntl(fd1, F_GETFL);
	fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);
	
	fd2_save = fcntl(fd2, F_GETFL);
	fcntl(fd2, F_SETFL,fd2_save|O_NONBLOCK);

	fsm12.state = STATE_R;
	fsm12.sfd = fd1;
	fsm12.dfd = fd2;
	fsm21.state = STATE_R;
	fsm21.sfd = fd2;
	fsm21.dfd = fd1;

	pfd[0].fd = fd1;
	pfd[1].fd = fd2;

	while(fsm12.state != STATE_T || fsm21.state != STATE_T){
		// 布置监视任务
		pfd[0].events = 0;				// 清空文件描述符集合
		pfd[1].events = 0;
		
		if(fsm12.state == STATE_R)		// 12 可读，监控sfd
			pfd[0].events |= POLLIN;
		if(fsm12.state == STATE_W)		// 12 可写，监控dfd
			pfd[1].events |= POLLOUT;

		if(fsm21.state == STATE_R)		// 21 可读，监控sfd
			pfd[1].events |= POLLIN;
		if(fsm21.state == STATE_W)		// 21 可写，监控dfd
			pfd[0].events |= POLLOUT;
		
		// 监视,直到事件发生
		if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO){	// 可读，可写的状态下，才进行文件监控
			while(poll(pfd, 2, -1) < 0){
				if(errno == EINTR)
					continue;
				perror("poll()");
				exit(1);
			}
		}
		// 查看监视结果
		if((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLOUT) || fsm12.state > STATE_AUTO)	// fd1可读，或者fd2可写，或者状态异常，推动状态机
			fsm_driver(&fsm12);
		if((pfd[1].revents & POLLIN) || (pfd[0].revents & POLLOUT) || fsm21.state > STATE_AUTO)	// fd2可读，或者fd1可写，或者状态异常，推动状态机
			fsm_driver(&fsm21);
	}

	fcntl(fd1, F_SETFL,fd1_save);
	fcntl(fd2, F_SETFL,fd2_save);

}

int main()
{
	int fd1, fd2;
	fd1 = open(TTY1, O_RDWR);
	if(fd1 < 0){
		perror("open()\n");
		exit(1);
	}
	write(fd1, "TTY1\n", 5);

	fd2 = open(TTY2, O_RDWR|O_NONBLOCK);
	if(fd2 < 0){
		perror("open()\n");
		exit(1);
	}
	write(fd2, "TTY2\n", 5);
	
	relay(fd1, fd2);

	close(fd1);
	close(fd2);

	exit(0);
}
